package net.luohuasheng.bee.proxy.mybatis.executor.processor;

import net.luohuasheng.bee.jdbc.tools.JdbcBuilder;
import net.luohuasheng.bee.jdbc.tools.common.utils.ObjectUtils;
import net.luohuasheng.bee.jdbc.tools.common.utils.ReflectionUtils;
import net.luohuasheng.bee.jdbc.tools.common.utils.execute.ParameterUtils;
import net.luohuasheng.bee.jdbc.tools.component.execute.ExecuteComponent;
import net.luohuasheng.bee.proxy.core.utils.CastUtils;
import net.luohuasheng.bee.proxy.core.utils.jpa.dto.JpaColumnDto;
import net.luohuasheng.bee.proxy.mybatis.common.exception.MybatisException;
import net.luohuasheng.bee.proxy.mybatis.common.utils.CrudUtils;
import net.luohuasheng.bee.proxy.mybatis.common.utils.MapperUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.List;

import static net.luohuasheng.bee.proxy.mybatis.executor.intercetor.BaseInterceptor.Index.MAPPED_STATEMENT_INDEX;
import static net.luohuasheng.bee.proxy.mybatis.executor.intercetor.BaseInterceptor.LANGUAGE_DRIVER;

/**
 * @author luohuasheng
 */
public class SaveOrUpdateByUniquesProcessor extends BaseSaveOrUpdateProcessor {


    public SaveOrUpdateByUniquesProcessor(Configuration configuration, Class<?> entityClass) {
        super(configuration, entityClass);
    }


    @Override
    protected void updateValue(Invocation invocation, MappedStatement ms, Object parameter) {
        if (parameter == null) {
            throw new MybatisException("empty value");
        }
        StringBuilder colStr = new StringBuilder();
        for (JpaColumnDto column : jpaTableDto.getColumns()) {
            if (!column.isPk() && column.isUpdatable()) {
                colStr.append("<if test=\"entity.").append(column.getField().getName()).append(" != null\">").append(column.getName()).append(" = #{entity.").append(column.getField().getName()).append("},").append("</if>");
            }
        }
        Object[] uniques = CastUtils.cast(ParameterUtils.getValue("uniques", parameter));

        String sql = "UPDATE " + jpaTableDto.getName() + " <set> " + colStr.toString() + " </set> " + addLogicConditions(jpaTableDto.getColumns()) + createUniquesSql(uniques, jpaTableDto.getColumns());
        SqlSource sqlSource = LANGUAGE_DRIVER.createSqlSource(ms.getConfiguration(), "<script>\n\t" + sql + "</script>", null);
        BoundSql newBoundSql = sqlSource.getBoundSql(parameter);
        invocation.getArgs()[MAPPED_STATEMENT_INDEX.getAnInt()] = MapperUtils.copyFromNewSql(ms, newBoundSql);
    }

    private String createUniquesSql(Object[] uniques, List<JpaColumnDto> columns) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for (JpaColumnDto column : columns) {
            if (ObjectUtils.anyEqual(column.getField().getName(), uniques)) {
                sb.append(" and ").append(column.getName()).append(" = #{entity.").append(column.getField().getName()).append("}");
                i++;
            }
        }
        return sb.toString();
    }

    @Override
    protected void insertValue(Invocation invocation, MappedStatement ms, Object parameter) throws Exception {
        if (parameter == null) {
            throw new MybatisException("empty value");
        }
        Object entity = ParameterUtils.getValue("entity", parameter);
        SqlSource sqlSource = insertSqlSource(ms, entity, "entity");
        BoundSql newBoundSql = sqlSource.getBoundSql(parameter);
        invocation.getArgs()[MAPPED_STATEMENT_INDEX.getAnInt()] = MapperUtils.copyFromNewSql(ms, newBoundSql);
    }

    @Override
    protected long getTotal(Invocation invocation, MappedStatement ms, Object parameter) throws Exception {
        Object[] uniques = CastUtils.cast(ParameterUtils.getValue("uniques", parameter));
        boolean isPk = false;
        for (JpaColumnDto column : jpaTableDto.getColumns()) {
            if (column.isPk()) {
                if (ObjectUtils.anyEqual(column.getName().toLowerCase(), Arrays.stream(uniques).map(o -> o.toString().toLowerCase()).toArray())) {
                    isPk = true;
                }
            }
        }
        Executor executor = (Executor) invocation.getTarget();
        SQL count = new SQL().FROM(jpaTableDto.getName()).SELECT("count(*)");
        for (JpaColumnDto column : jpaTableDto.getColumns()) {
            if (ObjectUtils.anyEqual(column.getField().getName(), uniques)) {
                count.WHERE(column.getName() + "=#{entity." + column.getField().getName() + "}");
            }
            if (!isPk && column.getTableLogic() != null) {
                count.WHERE(column.getName() + "='" + column.getTableLogic().noDelFlag() + "'");
            }
        }
        return getTotal(executor, ms, parameter, count.toString());
    }

    @Override
    public SqlSource createSqlSource() {
        return createSqlSource(SAVE_UPDATE);
    }


    @Override
    public KeyGenerator keyGenerator() {
        return getKey().getKeyGenerator();
    }

    @Override
    public String keyProperty() {
        return "entity." + getKey().getKeyProperty();
    }

    @Override
    public String keyColumn() {
        return getKey().getKeyProperty();
    }

    @Override
    protected String createPkSql(List<JpaColumnDto> columns) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for (JpaColumnDto column : columns) {
            if (column.isPk()) {
                sb.append(" and ").append(column.getName()).append(" = #{entity.").append(column.getField().getName()).append("}");
                i++;
            }
        }
        return sb.toString();
    }
}
